import { PromiseFn } from '@/@types/type';
import { getRealFileUrl } from '@/config/config';
import { message } from 'antd';
import { saveAs } from 'file-saver';

export const getParameters = () => {
  return new URLSearchParams(window.location.search);
};

export type FormatOption = { label: string | ((data: unknown) => string); value: string };
/**
 *
 * @param arr
 * @param props
 * @returns
 */
export function arrayFormater<T = Record<string, unknown>>(
  arr: T[] = [],
  props: FormatOption = {} as FormatOption
) {
  const fieldsProps = { ...{ label: 'label', value: 'value' }, ...props };

  return arr.map((i) => {
    if (typeof i !== 'object') {
      throw new Error('数组元素必须是一个对象');
    }
    const o: Record<string, any> = {};
    const keys = Object.keys(fieldsProps);
    keys.forEach((key) => {
      const keyValue = (fieldsProps as Record<string, string | ((data: unknown) => string)>)[key];
      if (typeof keyValue === 'function') {
        o[key] = keyValue(i);
      } else {
        o[key] = (i as any)[keyValue];
      }
    });
    return o;
  });
}
/**
 *
 * @param arr
 * @returns
 */
export function singleArrayFormater<T>(arr: T[]) {
  return arr.map((i) => {
    return {
      label: i,
      value: i
    };
  });
}

/**
 * 获取uuid
 * @returns {string} uuid
 */
export function getuuid(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c: string): string => {
    // eslint-disable-next-line no-bitwise
    const r = (Math.random() * 16) | 0;
    // eslint-disable-next-line no-bitwise
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}
/**
 * 根据 文件地址 获取文件名
 * @param name
 * @returns
 */
export function getFileName(name: string) {
  return name.substring(0, name.lastIndexOf('.'));
}
/**
 * 根据 文件地址 获取文件后缀
 * @param name
 * @returns
 */
export function getExtension(name: string) {
  return name.substring(name.lastIndexOf('.'));
}
/**
 /**
 * 通用文件下载
 * @param {string} url 文件下载地址
 * @param {object} option 下载配置
 */
export async function download(url: string, option: { filename?: string } = {}): Promise<void> {
  const realUrl = getRealFileUrl(url);
  const extension = getExtension(url);
  const { filename = getFileName(url) } = option;
  try {
    const blob = await fetch(realUrl).then((r) => r.blob());
    try {
      await saveAs(blob, `${filename}${extension}`);
    } catch (error) {
      if ((error as Error).name !== 'AbortError') {
        console.error(error);
      }
    }
  } catch (error) {
    message.warning('文件下载失败，请稍后重试');
  }
}

/**
 * 代理asyncawait写法的promise，为方法加上trycatch，并转化为固定对象返回，简化编写逻辑
 * 使用方法 const {data,ok,err } = await asyncFunction(fn,param1,param2)
 * @param {function} fn 需要被代理的promise方法
 * @param {array} params fn需要的参数
 * @returns {Promise} 一个promise，不会出现catch，使用返回值中的ok判断成功失败
 */
export async function asyncFunction<T>(
  fn: PromiseFn<any>,
  ...params: Parameters<typeof fn>
): Promise<{ ok: boolean; err?: any; data?: T }> {
  const res = { ok: false, data: undefined, err: undefined };
  try {
    res.data = (await fn(...(params as any[]))) as any;
    res.ok = true;
  } catch (error) {
    res.ok = false;
    res.err = error as any;
  }
  return res;
}
type EnumArrayItem = { label: string; value: any };

/**
 * 将对象转换成数组，主要用于将枚举对象转换
 * @param {T} data 需要转换的对象
 * @param {boolean} ignoreReverseMapping 是否忽略反射数据，针对枚举中的数字枚举
 * @param {(key:string,value:any)=>EnumArrayItem} customizer 自定义转换方法
 * @returns {array} 转换后的数组
 * @throw data应该是对象
 */
/**
 * 将对象转换成数组，主要用于将枚举对象转换
 * @param {T} data 需要转换的对象
 * @param {boolean} ignoreReverseMapping 是否忽略反射数据，针对枚举中的数字枚举
 * @param {function} customizer 自定义转换方法
 * @returns {array} 转换后的数组
 * @throw data应该是对象
 */
export function objectToArray<T extends Record<string, any>>(
  data: T,
  ignoreReverseMapping = true,
  customizer?: (key: string, value: any) => EnumArrayItem
) {
  if (typeof data !== 'object') {
    throw new Error(`data应该是一个对象，而现在它是一个 ${typeof data}`);
  }
  const res: EnumArrayItem[] = [];
  const entries = Object.entries(data);
  entries.forEach(([label, value]) => {
    if (ignoreReverseMapping) {
      const numberKey = Number(label);
      if (Number.isNaN(numberKey)) {
        res.push(customizer ? customizer(label, value) : { label, value });
      } else {
        if (value in data) {
          return;
        }
        res.push(customizer ? customizer(label, value) : { label, value });
      }
    } else {
      res.push(customizer ? customizer(label, value) : { label, value });
    }
  });
  return res;
}
